package org.jetlinks.pro.auth.dimension;

import lombok.AllArgsConstructor;
import org.hswebframework.web.authorization.Dimension;
import org.hswebframework.web.authorization.DimensionProvider;
import org.hswebframework.web.authorization.DimensionType;
import org.hswebframework.web.authorization.dimension.DimensionUserBind;
import org.hswebframework.web.authorization.dimension.DimensionUserBindProvider;
import org.hswebframework.web.authorization.simple.SimpleDimension;
import org.jetlinks.pro.auth.entity.TenantEntity;
import org.jetlinks.pro.auth.entity.TenantMemberEntity;
import org.jetlinks.pro.auth.enums.TenantMemberState;
import org.jetlinks.pro.auth.enums.TenantState;
import org.jetlinks.pro.auth.service.TenantMemberService;
import org.jetlinks.pro.auth.service.TenantService;
import org.jetlinks.pro.tenant.TenantConstants;
import org.jetlinks.pro.tenant.dimension.TenantDimensionType;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;

/**
 * 租户维度提供者实现,提供租户维度管理.
 * 在权限信息里可通过{@link org.hswebframework.web.authorization.Authentication#getDimension(String, String)}
 * 来获取对应的维度信息从而获取当前用户是在哪个租户中.
 *
 * @author zhouhao
 * @see DimensionProvider
 * @see TenantDimensionType
 * @see TenantMemberService
 * @see TenantService
 * @since 1.3
 */
@Component
@AllArgsConstructor
public class TenantDimensionProvider implements DimensionProvider, DimensionUserBindProvider {

    private final TenantMemberService memberService;

    private final TenantService tenantService;

    /**
     * 获取所有维度类型
     *
     * @return TenantDimensionType
     * @see TenantDimensionType
     */
    @Override
    public Flux<TenantDimensionType> getAllType() {
        return Flux.just(TenantDimensionType.tenantMember, TenantDimensionType.tenantCustomer);
    }

    /**
     * 根据用户id查询维度
     *
     * @param userId 用户ID
     * @return Dimension
     */
    @Override
    public Flux<Dimension> getDimensionByUserId(String userId) {
        return memberService
            .createQuery()
            .where(TenantMemberEntity::getUserId, userId)
            .and(TenantMemberEntity::getState, TenantMemberState.enabled)
            .fetch()
            .flatMap(this::createDimension)
            ;
    }

    /**
     * 根据维度类型和租户成员id查询维度
     *
     * @param type 维度类型
     * @param id   ID
     * @return Dimension
     * @see DimensionType
     */
    @Override
    public Mono<? extends Dimension> getDimensionById(DimensionType type, String id) {
        return memberService
            .createQuery()
            .where(TenantMemberEntity::getId, id)
            .and(TenantMemberEntity::getType, type.getId())
            .and(TenantMemberEntity::getState, TenantMemberState.enabled)
            .fetchOne()
            .flatMap(this::createDimension);
    }

    /**
     * 根据维度id查询用户id
     *
     * @param dimensionId
     * @return
     */
    @Override
    @SuppressWarnings("all")
    public Flux<String> getUserIdByDimensionId(String dimensionId) {
        return memberService
            .createQuery()
            .select(TenantMemberEntity::getUserId)
            .where(TenantMemberEntity::getTenantId, dimensionId)
            .fetch()
            .map(TenantMemberEntity::getUserId);

    }

    @Override
    public Flux<? extends Dimension> getDimensionsById(DimensionType type,
                                                       Collection<String> idList) {
        if (!TenantDimensionType.any(type)) {
            return Flux.empty();
        }
        return memberService
            .createQuery()
            .where()
            .in(TenantMemberEntity::getId, idList)
            .and(TenantMemberEntity::getType, type.getId())
            .and(TenantMemberEntity::getState, TenantMemberState.enabled)
            .fetch()
            .as(this::createDimension);
    }

    /**
     * 根据租户成员实体信息创建维度
     *
     * @param member 成员信息
     * @return 维度信息
     */
    private Mono<Dimension> createDimension(TenantMemberEntity member) {
        //调用租户管理服务查询租户信息
        return this
            .createDimension(Flux.just(member))
            .singleOrEmpty();
    }

    private Flux<Dimension> createDimension(Flux<TenantMemberEntity> members) {
        Flux<TenantMemberEntity> cache = members.cache();

        return cache
            .map(TenantMemberEntity::getTenantId)
            .as(tenantService::findById)
            .collectMap(TenantEntity::getId, Function.identity())
            .flatMapMany(tenantMap -> members
                .filter(member -> tenantMap.containsKey(member.getTenantId()))
                .map(member -> {
                    TenantEntity tenant = tenantMap.get(member.getTenantId());
                    //构建维度基本信息
                    SimpleDimension dimension = new SimpleDimension();
                    dimension.setId(tenant.getId());
                    dimension.setName(tenant.getName());
                    dimension.setType(TenantDimensionType.tenant);
                    Map<String, Object> opts = new HashMap<>();
                    opts.put(TenantConstants.memberId, member.getId());
                    opts.put(TenantConstants.admin, member.getAdminMember());
                    opts.put(TenantConstants.memberName, member.getName());
                    opts.put(TenantConstants.memberType, member.getType().name());
                    opts.put(TenantConstants.main, member.getMainTenant());
                    opts.put(TenantConstants.allDataAccess, member.hasAllDataAccess());
                    dimension.setOptions(opts);
                    return dimension;
                }));

    }

    @Override
    public Flux<DimensionUserBind> getDimensionBindInfo(Collection<String> userIdList) {

        return memberService
            .createQuery()
            .in(TenantMemberEntity::getUserId, userIdList)
            .fetch()
            .map(member -> DimensionUserBind.of(member.getUserId(), member.getType().getId(), member.getId()))
            ;
    }
}
